package cn.blnp.net.flowable.api.repository;

import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

/**
 * <h3>存储服务API案例演示</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/4/29 10:28
 */
@Slf4j
@SpringBootTest
public class RepositoryApiTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;

    /**
     *  <p>
     *      RepositoryService接口主要用于执行Flowable中与流程存储相关的数据操作，主要有以下4类:
     *  </p>
     *  <ol>
     *      <li>
     *          <b>流程部署相关操作：</b>包括对部署记录的创建、删除、查询等，主要操作部署记录表ACT_RE_ DEPLOYMENT。
     *      </li>
     *      <li>
     *          <b>流程定义相关操作：</b>包括对流程定义的查询、挂起、激活等，主要操作流程定义表ACT_RE_PROCDEF。
     *      </li>
     *      <li>
     *          <b>流程模型相关操作：</b>包括对模型的新建、保存、删除等，主要操作流程模型表ACT_RE_MODEL。
     *      </li>
     *      <li>
     *          <b>流程与发起人关系管理操作：</b>包括对候选人和候选组的新增、删除、查询等，主要操作流程人员关系表ACT_HI_IDENTITYLINK。
     *      </li>
     *  </ol>
     **/

    /**
     * <p><b>用途:部署流程定义<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 10:32 2025/4/29
     * @params []
       * @param
     * @return void
    **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                //流程定义名称
                .name("请假申请")
                .addClasspathResource("flow/demo.bpmn20.xml")
                .deploy();
        //再次查询流程定义
        ProcessDefinition processDefinition2 = repositoryService
                .createProcessDefinitionQuery()
                .deploymentId(deploy.getId()).singleResult();
        log.info("流程定义ID为：{}，流程名称为：{}，版本号：{}", processDefinition2.getId(),
                processDefinition2.getName(), processDefinition2.getVersion());

        /**
         *  演示结果：
         *  2025-04-29 10:55:22.185  INFO 39960 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：demoFlow:1:646ddad8-24a5-11f0-a048-50ebf6ec8f6d，流程名称为：demo，版本号：1
         **/
    }
    
    /**
     * <p><b>用途:流程定义删除操作<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 10:56 2025/4/29
     * @params [] 
     * @param  
     * @return void
    **/
    @Test
    public void test1() {
        /**
         *  RepositoryService接口中提供了两个删除流程定义的方法：
         *  1. deleteDeployment(String deploymentId)：删除指定流程定义的部署信息，不删除流程定义的流程实例。
         *  2. deleteDeployment(String deploymentId, boolean cascade)：删除指定流程定义的部署信息，并级联删除流程定义的流程实例。
         *
         *      第一个方法只有一个参数deploymentId，第二个方法增加了一个cascade参数，用于指定是否进行级联删除。不管是否指定进行级联删除，流程部署信息、流程定义数据、
         *  资源数据等都会被删除。如果指定进行级联删除，会同时删除流程对应的运行时流程实例、历史流程实例、关联的作业等。
         **/
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition("demoFlow:1:646ddad8-24a5-11f0-a048-50ebf6ec8f6d");
        log.info("流程定义ID为：{}，部署ID为：{}", processDefinition.getId(),
                processDefinition.getDeploymentId());

        //删除流程定义
        repositoryService.deleteDeployment(processDefinition.getDeploymentId());
        log.info("删除部署ID为{}的流程", processDefinition.getDeploymentId());

        //再次查询流程定义
        ProcessDefinition processDefinition2 = repositoryService
                .createProcessDefinitionQuery()
                .deploymentId(processDefinition.getDeploymentId())
                .singleResult();
        log.info("流程定义ID为：{}", (processDefinition2 == null ? "null" :
                processDefinition2.getId()));

        /**
         *  演示结果：
         *  2025-04-29 11:04:44.537  INFO 36404 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：demoFlow:1:646ddad8-24a5-11f0-a048-50ebf6ec8f6d，部署ID为：6420a535-24a5-11f0-a048-50ebf6ec8f6d
         *  2025-04-29 11:04:44.589  INFO 36404 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 删除部署ID为6420a535-24a5-11f0-a048-50ebf6ec8f6d的流程
         *  2025-04-29 11:04:44.591  INFO 36404 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：null
         **/
    }
    
    /**
     * <p><b>用途:挂起流程定义<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 11:06 2025/4/29
     * @params [] 
     * @param  
     * @return void
    **/
    @Test
    public void test2() {
        /**
         *  RepositoryService接口中提供了多种挂起流程定义的方法：
         *
         *  suspendProcessDefinitionById(String processDefinitionId)：根据流程定义ID立即挂起
         *  suspendProcessDefinitionById(String processDefinitionId, boolean suspendProcessInstances, Date suspensionDate)：根据流程定义ID在指定时间挂起
         *  suspendProcessDefinitionByKey(String processDefinitionKey)：根据流程定义KEY立即挂起
         *  suspendProcessDefinitionByKey(String processDefinitionKey, boolean suspendProcessInstances, Date suspensionDate)：根据流程定义KEY在指定时间挂起
         *  suspendProcessDefinitionByKey(String processDefinitionKey, String tenantId):  根据流程定义KEY和租户ID立即挂起
         *  suspendProcessDefinitionByKey(String processDefinitionKey, boolean suspendProcessInstances, Date suspensionDate, String tenantId): 根据流程定义KEY、租户ID和指定时间挂起
         **/
        Deployment deploy = repositoryService.createDeployment()
                //流程定义名称
                .name("请假申请")
                .addClasspathResource("flow/demo.bpmn20.xml")
                .deploy();
        //再次查询流程定义
        ProcessDefinition procDef = repositoryService
                .createProcessDefinitionQuery()
                .deploymentId(deploy.getId()).singleResult();
        log.info("流程定义ID为：{}，流程名称为：{}，版本号：{}", procDef.getId(), procDef.getName(), procDef.getVersion());
        //查询流程定义状态
        queryProcessDefinition(procDef.getId());
        //发起流程实例
        startProcessInstance(procDef.getId());

        //挂起流程定义
        repositoryService.suspendProcessDefinitionById(procDef.getId());
        log.info("挂起ID为{}的流程定义", procDef.getId());
        //再次查询流程定义状态
        queryProcessDefinition(procDef.getId());
        //再次发起流程实例
        startProcessInstance(procDef.getId());

        /**
         *  演示结果：
         *  2025-04-29 11:14:27.806  INFO 70540 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d，流程名称为：demo，版本号：1
         *  2025-04-29 11:14:27.808  INFO 70540 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d，流程定义key为：demoFlow，是否挂起：false
         *  2025-04-29 11:14:27.879  INFO 70540 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 发起的流程实例ID为：0f4d86b7-24a8-11f0-a472-50ebf6ec8f6d，流程定义ID为：demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d，流程定义key为：demoFlow
         *  2025-04-29 11:14:27.895  INFO 70540 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d，流程定义key为：demoFlow，是否挂起：true
         *  2025-04-29 11:14:27.906 ERROR 70540 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 发起流程实例失败，错误原因：Cannot start process instance. Process definition demo (id = demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d) is suspended
         **/
    }

    /**
     * <p><b>用途:查询流程定义状态<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 11:12 2025/4/29
     * @params [procDefId] 
     * @param procDefId 
     * @return void
    **/
    private void queryProcessDefinition(String procDefId) {
        ProcessDefinition procDef = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(procDefId)
                .singleResult();
        log.info("流程定义ID为：{}，流程定义key为：{}，是否挂起：{}", procDef.getId(),
                procDef.getKey(), procDef.isSuspended());
    }
    
    /**
     * <p><b>用途:根据流程定义ID发起流程<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 11:12 2025/4/29
     * @params [procDefId] 
     * @param procDefId 
     * @return void
    **/
    private void startProcessInstance(String procDefId) {
        try {
            ProcessInstance procInst = runtimeService
                    .startProcessInstanceById(procDefId);
            log.info("发起的流程实例ID为：{}，流程定义ID为：{}，流程定义key为：{}",
                    procInst.getId(), procInst.getProcessDefinitionId(),
                    procInst.getProcessDefinitionKey());
        } catch (Exception e) {
            log.error("发起流程实例失败，错误原因：{}", e.getMessage());
        }
    }

    /**
     * <p><b>用途:激活流程定义<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 11:16 2025/4/29
     * @params []
     * @param
     * @return void
    **/
    @Test
    public void test3() {
        /**
         *  流程定义激活的API方式与挂起的API方式类似，只是将suspendXxx()方法换成activateXxx()方法即可。
         **/
        //查询流程定义
        ProcessDefinition procDef = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId("demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d")
                .singleResult();
        //查询流程定义状态
        queryProcessDefinition(procDef.getId());
        if (!procDef.isSuspended()) {
            //挂起流程定义
            repositoryService.suspendProcessDefinitionById(procDef.getId());
            log.info("挂起ID为{}的流程定义", procDef.getId());
            //再次查询流程定义状态
            queryProcessDefinition(procDef.getId());
        }
        //激活流程定义
        repositoryService.activateProcessDefinitionById(procDef.getId());
        log.info("激活ID为{}的流程定义", procDef.getId());
        //再次查询流程定义状态
        queryProcessDefinition(procDef.getId());

        /**
         *  演示结果：
         *  2025-04-29 11:21:24.376  INFO 33632 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d，流程定义key为：demoFlow，是否挂起：true
         *  2025-04-29 11:21:24.410  INFO 33632 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 激活ID为demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d的流程定义
         *  2025-04-29 11:21:24.416  INFO 33632 --- [           main] c.b.n.f.a.repository.RepositoryApiTest   : 流程定义ID为：demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d，流程定义key为：demoFlow，是否挂起：false
         **/
    }
}
